package com.etc.esdemo.controller;

import com.alibaba.fastjson.JSON;
import com.etc.esdemo.model.Person;
import com.etc.esdemo.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("person")
public class PersonController {

    @Autowired
    private PersonRepository repository;

    /**
     * 添加
     *
     * @return
     */
    @GetMapping("save")
    public Person save() {
        Person person = new Person(1L, "刘备", "蜀国", 18, new Date(), "刘备（161年－223年6月10日），即汉昭烈帝（221年－223年在位），又称先主，字玄德，东汉末年幽州涿郡涿县（今河北省涿州市）人，西汉中山靖王刘胜之后，三国时期蜀汉开国皇帝、政治家。\n刘备少年时拜卢植为师；早年颠沛流离，备尝艰辛，投靠过多个诸侯，曾参与镇压黄巾起义。先后率军救援北海相孔融、徐州牧陶谦等。陶谦病亡后，将徐州让与刘备。赤壁之战时，刘备与孙权联盟击败曹操，趁势夺取荆州。而后进取益州。于章武元年（221年）在成都称帝，国号汉，史称蜀或蜀汉。《三国志》评刘备的机权干略不及曹操，但其弘毅宽厚，知人待士，百折不挠，终成帝业。刘备也称自己做事“每与操反，事乃成尔”。\n章武三年（223年），刘备病逝于白帝城，终年六十三岁，谥号昭烈皇帝，庙号烈祖，葬惠陵。后世有众多文艺作品以其为主角，在成都武侯祠有昭烈庙为纪念。");
        Person save = repository.save(person);
        log.info("【save】= {}", save);
        return save;
    }

    /**
     * 批量添加
     *
     * @return
     */
    @GetMapping("saveList")
    public Iterable<Person> saveList() {
        //创建一个list集合把数据添加进去
        List<Person> personList = new ArrayList<>();
        personList.add(new Person(2L, "曹操", "魏国", 20, new Date(), "曹操（155年－220年3月15日），字孟德，一名吉利，小字阿瞒，沛国谯县（今安徽亳州）人。东汉末年杰出的政治家、军事家、文学家、书法家，三国中曹魏政权的奠基人。\n曹操曾担任东汉丞相，后加封魏王，奠定了曹魏立国的基础。去世后谥号为武王。其子曹丕称帝后，追尊为武皇帝，庙号太祖。\n东汉末年，天下大乱，曹操以汉天子的名义征讨四方，对内消灭二袁、吕布、刘表、马超、韩遂等割据势力，对外降服南匈奴、乌桓、鲜卑等，统一了中国北方，并实行一系列政策恢复经济生产和社会秩序，扩大屯田、兴修水利、奖励农桑、重视手工业、安置流亡人口、实行“租调制”，从而使中原社会渐趋稳定、经济出现转机。黄河流域在曹操统治下，政治渐见清明，经济逐步恢复，阶级压迫稍有减轻，社会风气有所好转。曹操在汉朝的名义下所采取的一些措施具有积极作用。\n曹操军事上精通兵法，重贤爱才，为此不惜一切代价将看中的潜能分子收于麾下；生活上善诗歌，抒发自己的政治抱负，并反映汉末人民的苦难生活，气魄雄伟，慷慨悲凉；散文亦清峻整洁，开启并繁荣了建安文学，给后人留下了宝贵的精神财富，鲁迅评价其为“改造文章的祖师”。同时曹操也擅长书法，唐朝张怀瓘在《书断》将曹操的章草评为“妙品”。"));
        personList.add(new Person(3L, "孙权", "吴国", 19, new Date(), "孙权（182年－252年5月21日），字仲谋，吴郡富春（今浙江杭州富阳区）人。三国时代孙吴的建立者（229年－252年在位）。\n孙权的父亲孙坚和兄长孙策，在东汉末年群雄割据中打下了江东基业。建安五年（200年），孙策遇刺身亡，孙权继之掌事，成为一方诸侯。建安十三年（208年），与刘备建立孙刘联盟，并于赤壁之战中击败曹操，奠定三国鼎立的基础。建安二十四年（219年），孙权派吕蒙成功袭取刘备的荆州，使领土面积大大增加。\n黄武元年（222年），孙权被魏文帝曹丕册封为吴王，建立吴国。同年，在夷陵之战中大败刘备。黄龙元年（229年），在武昌正式称帝，国号吴，不久后迁都建业。孙权称帝后，设置农官，实行屯田，设置郡县，并继续剿抚山越，促进了江南经济的发展。在此基础上，他又多次派人出海。黄龙二年（230年），孙权派卫温、诸葛直抵达夷州。\n孙权晚年在继承人问题上反复无常，引致群下党争，朝局不稳。太元元年（252年）病逝，享年七十一岁，在位二十四年，谥号大皇帝，庙号太祖，葬于蒋陵。\n孙权亦善书，唐代张怀瓘在《书估》中将其书法列为第三等。"));
        personList.add(new Person(4L, "诸葛亮", "蜀国", 16, new Date(), "诸葛亮（181年-234年10月8日），字孔明，号卧龙，徐州琅琊阳都（今山东临沂市沂南县）人，三国时期蜀国丞相，杰出的政治家、军事家、外交家、文学家、书法家、发明家。\n早年随叔父诸葛玄到荆州，诸葛玄死后，诸葛亮就在襄阳隆中隐居。后刘备三顾茅庐请出诸葛亮，联孙抗曹，于赤壁之战大败曹军。形成三国鼎足之势，又夺占荆州。建安十六年（211年），攻取益州。继又击败曹军，夺得汉中。蜀章武元年（221年），刘备在成都建立蜀汉政权，诸葛亮被任命为丞相，主持朝政。蜀后主刘禅继位，诸葛亮被封为武乡侯，领益州牧。勤勉谨慎，大小政事必亲自处理，赏罚严明；与东吴联盟，改善和西南各族的关系；实行屯田政策，加强战备。前后六次北伐中原，多以粮尽无功。终因积劳成疾，于蜀建兴十二年（234年）病逝于五丈原（今陕西宝鸡岐山境内），享年54岁。刘禅追封其为忠武侯，后世常以武侯尊称诸葛亮。东晋政权因其军事才能特追封他为武兴王。\n诸葛亮散文代表作有《出师表》《诫子书》等。曾发明木牛流马、孔明灯等，并改造连弩，叫做诸葛连弩，可一弩十矢俱发。诸葛亮一生“鞠躬尽瘁、死而后已”，是中国传统文化中忠臣与智者的代表人物。"));
        //把list集合放入迭代器
        Iterable<Person> people = repository.saveAll(personList);
        log.info("【people】={}", people);
        return people;
    }

    /**
     * 删除
     */
    @GetMapping("delete")
    public void delete() {
        //根据id删除
        // repository.deleteById(1L);
        //对象删除
        //匿名内部类
//        repository.findById(3L).ifPresent(new Consumer<Person>() {
//            @Override
//            public void accept(Person ps) {
//                repository.delete(ps);
//            }
//        });
        // 函数是编程
//        repository.findById(4L).ifPresent(person -> repository.delete(person));
        repository.deleteAll(repository.findAll());
    }

    /**
     * 更新
     */
    @GetMapping("update")
    public void update() {
        repository.findById(1L).ifPresent(person -> {
            person.setRemark(person.getRemark() + "\n我是你爸爸！！！");
            repository.save(person);
        });
    }

    /**
     * 普通查询(按照年龄倒序)
     */
    @GetMapping("select")
    public void select() {
        repository.findAll(Sort.by(Sort.Direction.DESC, "age"))
                .forEach(person -> log.info("{} 年龄:{}", person.getName(), person.getAge()));
    }

    /**
     * 根据年龄范围查询
     */
    @GetMapping("selectAge")
    public void selectAge() {
        repository.findByAgeBetween(18, 19).forEach(person -> log.info("{} 年龄: {}", person.getName(), person.getAge()));
    }

    /**
     * 高级查询
     */
    @GetMapping("gjSelect")
    public void gjSelect(String name) {
        //QueryBuilders 提供了很多静态方法，可以实现大部分查询条件的封装
        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("name", name);
        log.info("【queryBuilder】= {}", queryBuilder.toString());

        repository.search(queryBuilder).forEach(person -> log.info("【person】= {}", person));

    }

    @GetMapping("advancedSelect")
    public void advancedSelect() {
        //构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //添加基本的分词条件
        queryBuilder.withQuery(QueryBuilders.matchQuery("remark", "刘备"));
        //排序条件
        queryBuilder.withSort(SortBuilders.fieldSort("age").order(SortOrder.DESC));
        //分页条件
        queryBuilder.withPageable(PageRequest.of(0, 2));
        Page<Person> people = repository.search(queryBuilder.build());
        log.info("【people】总条数 = {}", people.getTotalElements());
        log.info("【people】总条数 = {}", people.getTotalPages());
        people.forEach(person -> log.info("【people】= {}，年龄 = {}", person.getName(), person.getAge()));
    }

    /**
     * 聚合，平均年龄
     */
    @GetMapping("avg")
    public void avg() {
        //构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
        //平均年龄

        queryBuilder.addAggregation(AggregationBuilders.avg("avg").field("age"));

        log.info("【queryBuilder】 = {}", JSON.toJSONString(queryBuilder.build()));

        AggregatedPage<Person> page = (AggregatedPage<Person>) repository.search(queryBuilder.build());
        double avg = ((InternalAvg) page.getAggregation("avg")).getValue();
        log.info("【avg】 = {} ", avg);
    }

    /**
     * 高级聚合查询，每个国家的人有几个，每个国家的平均年龄是多少
     */
    @GetMapping("advancedAvg")
    public void advancedAvg() {
        //构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //不查询任何结果
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
        // 添加一个新的聚合，聚合类型为terms，聚合名称为country，聚合字段为age
        queryBuilder.addAggregation(AggregationBuilders.terms("country").field("country")
                .subAggregation(AggregationBuilders.avg("avg").field("age")));
        //在国家聚合桶内进行嵌套聚合，求平均年龄
        log.info("【queryBuilder】 = {}", JSON.toJSONString(queryBuilder.build()));
        //查询
        AggregatedPage<Person> people = (AggregatedPage<Person>) repository.search(queryBuilder.build());
        //解析
        // 从结果中取出名为 country 的那个聚合，因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
        StringTerms country = (StringTerms) people.getAggregation("country");
        //获取桶
        List<StringTerms.Bucket> buckets = country.getBuckets();
        for (StringTerms.Bucket bucket : buckets) {
            //获取桶中的key，即国家名称  4.4. 获取桶中的文档数量
            log.info("{}总共有{}人",bucket.getKeyAsString(),bucket.getDocCount());
            //获取子聚合结果：
            InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("avg");
            log.info("平均年龄:{}", avg);
        }
    }
    @GetMapping("advanceAgg")
    public void advanceAgg() {
        // 构造查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 不查询任何结果
        // 1. 添加一个新的聚合，聚合类型为terms，聚合名称为country，聚合字段为age
        queryBuilder.addAggregation(AggregationBuilders.terms("country").field("country.keyword")
                // 2. 在国家聚合桶内进行嵌套聚合，求平均年龄
                .subAggregation(AggregationBuilders.avg("avg").field("age")));

        log.info("【queryBuilder】= {}", JSON.toJSONString(queryBuilder.build()));

        // 3. 查询
        AggregatedPage<Person> people = (AggregatedPage<Person>) repository.search(queryBuilder.build());

        // 4. 解析
        // 4.1. 从结果中取出名为 country 的那个聚合，因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
        StringTerms country = (StringTerms) people.getAggregation("country");
        // 4.2. 获取桶
        List<StringTerms.Bucket> buckets = country.getBuckets();
        for (StringTerms.Bucket bucket:buckets) {
            // 4.3. 获取桶中的key，即国家名称  4.4. 获取桶中的文档数量
            log.info("{} 总共有 {} 人", bucket.getKeyAsString(), bucket.getDocCount());
            // 4.5. 获取子聚合结果：
            InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("avg");
            log.info("平均年龄：{}", avg.getValue());
        }
    }
    @GetMapping("jh2Search")
    public void jh2Search(){
        //创建查询条件构造器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("country").field("country")
                //创建子聚合工程（对父聚合工程的结果进行求取平均值）
                .subAggregation(AggregationBuilders.avg("avg").field("age"))
        );

        //查询
        AggregatedPage<Person> search = (AggregatedPage<Person>)repository.search(nativeSearchQueryBuilder.build());
        //根据聚合名称获取对应聚合
        StringTerms brands = (StringTerms)search.getAggregation("country");
        //获取查询到的桶
        List<StringTerms.Bucket> buckets = brands.getBuckets();

        for (StringTerms.Bucket b:buckets) {
            //获取父聚合的字段名
            System.out.print("品牌名称："+b.getKeyAsString() + "查到的文档数："+b.getDocCount());
            //获取父聚合中桶中数量
            System.out.println();
            //获取子聚合，转为Map集合，获取构建子聚合时价格平均值的key，这里指定的是avg（强转为平均值）。
            InternalAvg avg = (InternalAvg)b.getAggregations().asMap().get("avg");
            System.out.println("\t\t平均价格是："+avg.getValue());
        }

    }
}
